package org.atlantis.uzw.base.redis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.atlantis.uzw.base.redis.RedisTemplate.RedisAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;

public class RedisScriptExecutor {

	private static Logger log = LoggerFactory.getLogger(RedisScriptExecutor.class);

	private RedisTemplate redisTemplate;

	private Map<String, String> scriptCache = new HashMap<>();

	public void setRedisTemplate(RedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	public synchronized String load(final String script) {
		String hash = redisTemplate.execute(new RedisAction<String>() {
			@Override
			public String action(Jedis jedis) {
				return jedis.scriptLoad(script);
			}
		});
		scriptCache.put(hash, script);
		return hash;
	}

	public Object execute(final String hash, final List<String> keys, final List<String> args) {
		if (!scriptCache.containsKey(hash)) throw new IllegalArgumentException("Script hash " + hash + " is not loaded");

		try {
			return redisTemplate.execute(new RedisAction<Object>() {
				@Override
				public Object action(Jedis jedis) {
					return jedis.evalsha(hash, keys, args);
				}
			});
		} catch (JedisDataException e) {
			log.warn("Script hash {} is not loaded yet, try to reload and run it again");
			return reloadAndExecute(hash, keys, args);
		}
	}

	private Object reloadAndExecute(final String hash, final List<String> keys, final List<String> args) {
		return redisTemplate.execute(new RedisAction<Object>() {
			@Override
			public Object action(Jedis jedis) {
				String script = scriptCache.get(hash);
				jedis.scriptLoad(script);
				return jedis.evalsha(hash, keys, args);
			}
		});
	}
}
